// Copyright  2004-2008 Backplane Ltd.
//
// This file is part of Ubiquity formsPlayer
//
// Ubiquity formsPlayer is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3 of the License, or (at your
// option) any later version.
//
// Ubiquity formsPlayer is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License
// along with Ubiquity formsPlayer If not, see <http://www.gnu.org/licenses/>.
//	
//
//   Purpose:  comparing xml nodes for equality..
//   $Id$

#include "stdafx.h"
#include "isEqualNode.h"
#include "ComCompare.h"

bool compareNames(IXMLDOMNode * pNodeLHS,IXMLDOMNode * pNodeRHS)
{
	ASSERT(pNodeLHS != NULL && pNodeRHS != NULL);

	CComBSTR sLHS,sRHS;
	pNodeLHS->get_baseName(&sLHS);
	pNodeRHS->get_baseName(&sRHS);
	bool bReturn = (sRHS == sLHS);
	//Names were OK, now check namespace.
	if(bReturn)
	{
		pNodeLHS->get_namespaceURI(&sLHS);
		pNodeRHS->get_namespaceURI(&sRHS);
		bReturn = (sRHS == sLHS);
	}
	return bReturn;
}

bool compareAttrList(IXMLDOMNode * pNodeLHS,IXMLDOMNode * pNodeRHS)
{
	ASSERT(pNodeLHS != NULL && pNodeRHS != NULL);
	bool bReturn = true;	
	CComPtr<IXMLDOMNamedNodeMap> pnMapLHS, pnMapRHS;
	pNodeLHS->get_attributes(&pnMapLHS);
	pNodeRHS->get_attributes(&pnMapRHS);
	
	if(pnMapLHS && pnMapRHS)
	{
		//check list size, to save running through the 
		//	list doing comparisons.  Also saves an extra 
		//	check in the loop
		long lLHS, lRHS;
		pnMapLHS->get_length(&lLHS);
		pnMapRHS->get_length(&lRHS);
		if(lLHS == lRHS)
		{
			//list lengths match compare members
			//TODO: possibly make this insensitive to order.
			CComPtr<IXMLDOMNode> pAttrLHS, pAttrRHS;

			pnMapLHS->nextNode(&pAttrLHS);
			pnMapRHS->nextNode(&pAttrRHS);
			
			CComBSTR sLHS, sRHS;	
			while(bReturn && pAttrLHS)
			{
				bReturn = compareNames(pAttrLHS, pAttrRHS);
				if(bReturn)
				{
					CComBSTR sLHS, sRHS;
					pAttrLHS->get_text(&sLHS);
					pAttrRHS->get_text(&sRHS);
					bReturn = (sLHS == sRHS);
					if(bReturn)
					{
						pAttrLHS.Release();
						pAttrRHS.Release();
						pnMapLHS->nextNode(&pAttrLHS);
						pnMapRHS->nextNode(&pAttrRHS);
					}
				}
			}
		}
		else
		{
			bReturn = false;
		}
	}
	else
	{
		//nodemap was not returned by at least one
		// of the nodes.  make sure that it was not
		// returned by either, NULL == NULL

		if(pnMapLHS || pnMapRHS)
		{
			bReturn = false;
		}
	}
	return bReturn;
}


//compares two noes to see if they look the same
bool isEqualNode(IXMLDOMNode * pNodeLHS, IXMLDOMNode * pNodeRHS, bool bDeep)
{
	ASSERT(pNodeLHS != NULL && pNodeRHS != NULL);
	//It would be silly to compare a node to itself.
	//	don't do it!.
	ASSERT(!::COMCompare(pNodeLHS,pNodeRHS));
	
	bool bReturn = false;
	//Test for equality of type.
	DOMNodeType typeRHS, typeLHS;
	pNodeLHS->get_nodeType(&typeLHS);
	pNodeRHS->get_nodeType(&typeRHS);

	if(typeLHS == typeRHS)
	{
		//if they are equal,
		//	pass on to specific type checker
		switch(typeLHS)
		{
			case NODE_INVALID:
				bReturn = false;
				break;
			case NODE_ELEMENT:
			{
				bReturn = compareNames(pNodeLHS, pNodeRHS);
				if(!bReturn)
					break;
				CComQIPtr<IXMLDOMElement> pElLHS = pNodeLHS;
				CComQIPtr<IXMLDOMElement> pElRHS = pNodeRHS;
				bReturn = compareAttrList(pElLHS, pElRHS);
				if(!bReturn)
					break;
				//Deliberate fallthrough to Document and 
				//	documentFragment to check children.
			}
			case NODE_DOCUMENT:
			case NODE_DOCUMENT_FRAGMENT:
				if(bDeep)
				{
					CComPtr<IXMLDOMNodeList> pNlLHS,pNlRHS;
					pNodeLHS->get_childNodes(&pNlLHS);
					pNodeRHS->get_childNodes(&pNlRHS);
					bReturn = compareNodeLists(pNlLHS,pNlRHS, bDeep);
				}
				break;
			case NODE_ATTRIBUTE:
				bReturn = compareNames(pNodeLHS, pNodeRHS);
				{
					CComBSTR sLHS, sRHS;
					pNodeLHS->get_text(&sLHS);
					pNodeRHS->get_text(&sRHS);
					bReturn = (sLHS == sRHS);
				}
				break;
			//just check the xml of these, they have no children
			//	and are not namespace aware.
			case NODE_PROCESSING_INSTRUCTION:
			case NODE_TEXT:
			case NODE_CDATA_SECTION:
			case NODE_COMMENT:
			case NODE_DOCUMENT_TYPE:
			case NODE_NOTATION:	
			{
				CComBSTR sLHS, sRHS;
				pNodeLHS->get_xml(&sLHS);
				pNodeRHS->get_xml(&sRHS);
				bReturn = (sLHS == sRHS);
				break;
			}
			//ignore these, NOTIMPL, possibly implement later 
			// when I know how to compare them
			case NODE_ENTITY_REFERENCE:
			case NODE_ENTITY:
				bReturn = true;
		}
	}
	return bReturn;
}


bool compareNodeLists(IXMLDOMNodeList * pNlLHS,IXMLDOMNodeList * pNlRHS, bool bDeep)
{
	ASSERT(pNlLHS != NULL && pNlRHS != NULL);

	//Check the lengths
	long lLHS, lRHS;
	pNlLHS->get_length(&lLHS);
	pNlRHS->get_length(&lRHS);
	bool bReturn = lLHS == lRHS;
	if(bReturn)
	{
		//compare all children to see if they are equal 
		CComPtr<IXMLDOMNode> pNodeLHS, pNodeRHS;
		pNlLHS->nextNode(&pNodeLHS);
		pNlRHS->nextNode(&pNodeRHS);
		while(bReturn && pNodeLHS)
		{
			bReturn  = isEqualNode(pNodeLHS,pNodeRHS,bDeep);
			pNodeLHS.Release();
			pNodeRHS.Release();
			pNlLHS->nextNode(&pNodeLHS);
			pNlRHS->nextNode(&pNodeRHS);

		}
	}
	return bReturn;
		
}
